/*
 * Copyright (C) 2009 Rafael Fernandes
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.phonebooksharing.android.widgets.pagination;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;

import android.content.Context;
import android.database.DataSetObserver;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.phonebooksharing.android.R;
import com.phonebooksharing.android.util.Global;
import com.phonebooksharing.android.util.NetworkHandlerTask;
import com.phonebooksharing.dto.dashboard.DashboardReaderItem;

@SuppressWarnings("unchecked")
public abstract class PaginatorListView extends ListView {
    
    private ProgessbarListener progressListener;
//    protected FastBitmapDrawable loading;
//    private Animation anim;
    protected int qty;
    
    public PaginatorListView(Context context, AttributeSet attrs) {
        this(context, 0, null);
    }

    public PaginatorListView(Context dv, int qty, TextView counter) {
        super(dv);
        
        setCacheColorHint(0);
        if(dv instanceof ProgessbarListener) progressListener = (ProgessbarListener)dv;
        
        this.qty = qty;
        
//        loading = ImageUtilities.loadingBitmap;
//        anim = AnimationUtils.loadAnimation(dv, R.anim.magnify);
        
//        setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
//            @Override
//            public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
//                v.findViewById(R.id.icon).startAnimation(anim);
//            }
//
//            @Override
//            public void onNothingSelected(AdapterView<?> parent) {
//            }
//        });
        adapter = new InternalAdapter(getContext());
        
        this.counter = counter;
        adapter.registerDataSetObserver(new CounterUpdater());

    }
    
    public void load() {
        load(qty);
    }
    
    public void load(int q) {
        if(adapter.init) return;
        adapter.init = true;
        
        Log.d(Global.LOG_TAG, "Loading " + this);

        addFooterView(adapter.footerView, null, false);
        setAdapter(adapter);
        
        adapter.totalResults = q;
        setOnScrollListener(adapter);//itself triggers the page loader
    }

    protected InternalAdapter adapter;
    protected int currentPage = 0;
    private static final int FORCE_TOP = 2, REMOVE_FOOTER = 3;
    
    private Handler resultsUpdated = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == FORCE_TOP)
                setSelection(0);
            if(msg.what == REMOVE_FOOTER) {
                removeFooterView(adapter.footerView);
                requestLayout();
            }
//            removeFooterView(adapter.footerView);
            adapter.notifyDataSetChanged();
        }
    };

    protected class InternalAdapter extends BaseAdapter implements OnScrollListener {
        private ArrayList<DashboardReaderItem<?>> list;
        private LayoutInflater inflater;
        public final View footerView;
        private boolean firstFetch = true, 
                        fetchRequested = false,
                        hasServerError = false;
        public boolean init = false;
        public long totalResults = 1;
        
        public InternalAdapter(Context c) {
            inflater = LayoutInflater.from(c);
            list = new ArrayList();
            footerView = inflater.inflate(R.layout.loading, null, false);
        }
        
        public ArrayList<DashboardReaderItem<?>> getInternalList() {
            return list;
        }
        
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int loc) {
            if(loc < list.size())
                return list.get(loc);
            return null;
        }

        @Override
        public long getItemId(int id) {
            return id;
        }
        
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return getConvertView(convertView, list.get(position), position, parent);
        }
        
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            // trigger more search results when hitting the last item
            if(fetchRequested || hasServerError) return;
            if(firstFetch || firstVisibleItem + visibleItemCount == totalItemCount) {
                triggerPage();
            }
        }
        
        public void onScrollStateChanged(AbsListView view, int scrollState) {}
        
        public void searchDone() {
            // force update of cursor data sources
            resultsUpdated.sendEmptyMessage(firstFetch ? FORCE_TOP : -1);
            
            if(hasServerError) {
                Toast.makeText(PaginatorListView.this.getContext(), R.string.paginator_server_error, Toast.LENGTH_LONG).show();
                return;
            }
            
            final int size = list.size();
            if(size < totalResults && totalResults > Global.PAGINATOR_PAGE_SIZE) {
                fetchRequested = false;
                firstFetch = false;
//                Collections.sort(list);
//                notifyDataSetChanged();
            } else {
                resultsUpdated.sendEmptyMessage(REMOVE_FOOTER);
            }
        }
        
        public void triggerPage() {
            fetchRequested = true;
            // trigger fetch of next page of results if available
            // TODO: somehow make sure we dont double-trigger this from the gui
            if(totalResults >= getCount() + 1) {
                
                Log.i("PBS", "triggering page! totalResults = " + totalResults + ", getCount() + 1 = " + (getCount() + 1));
                
                new LoadDataTask().execute();
            } else {
                // change our footer view to say no more results
                resultsUpdated.sendEmptyMessage(REMOVE_FOOTER);
            }
        }
    }
    
    abstract public View getConvertView(View convertView, Object dto, int position, ViewGroup parent);
    abstract public String getLoadDataURL();
    abstract public Serializable getObject2Send();

    class LoadDataTask extends NetworkHandlerTask<String, Object, Void> {
        @Override
        public void onPreExecute() {
            if(progressListener != null) progressListener.setProgressStatus(View.VISIBLE);
        }

        @Override
        public Void doInBackground(String... params) {
            try {
                ArrayList al = adapter.list;
                ArrayList list = (ArrayList)doNetworkPost(getLoadDataURL(), getObject2Send());
                for(Object ri : list) {
                    al.add(ri);
//                    publishProgress(0);
                }
            } catch (IOException e) {
                adapter.hasServerError = true;
                Log.e(Global.LOG_TAG, "are you connected to internet???"); //TODO: send it to user
                e.printStackTrace();
                cancel(true);
            } catch (Exception e) {
                adapter.hasServerError = true;
                // TODO: handle exception
                e.printStackTrace();
                cancel(true);
            }
            return null;
        }
        
        @Override
        public void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            adapter.searchDone();
            if(progressListener != null) progressListener.setProgressStatus(View.GONE);
        }
    }
    
    private TextView counter;
    class CounterUpdater extends DataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            long r = adapter.totalResults, l = adapter.getCount();
            if(l > r) {
                l = r;
            }
            counter.setText("(" + l + " of " + r + ")");
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            long r = adapter.totalResults, l = adapter.getCount();
            if(l > r) {
                l = r;
            }
            counter.setText("(" + l + " of " + r + ")");
        }
    }
}
